package com.auditlog.listener;

import cn.hutool.core.collection.CollectionUtil;
import com.auditlog.datasource.context.ExecutionContext;
import com.auditlog.datasource.db.SqlType;
import com.auditlog.datasource.struct.*;
import com.auditlog.format.RecordImageFormat;
import com.auditlog.format.RecordImageFormatRegistry;
import com.google.common.eventbus.Subscribe;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
public class ContextChangeEventSubscriber implements EventSubscriber<ContextChangeEvent> {

    @Override
    @Subscribe
    public void onEvent(ContextChangeEvent contextChangeEvent) {
        try {
            ExecutionContext context = contextChangeEvent.getContext();
            if (context.isError()) {
                log.warn("日志解析过程中出现异常", context.getCause());
                return;
            }
            if (context.isTargetError()) {
                log.warn("目标sql执行发生异常：{}", context.getTargetCause().getMessage());
            }
            List<TableChangeLog> tableChangeLogs = new ArrayList<>();
            Map<String, List<RecordImage>> tableRecordImageMap = context.getExecutedRecordImages().stream().collect(Collectors.groupingBy(recordImage -> recordImage.getTableMeta().getTableName().toUpperCase(Locale.ROOT), Collectors.toList()));
            for (Map.Entry<String, List<RecordImage>> recordEntry : tableRecordImageMap.entrySet()) {
                String tableName = recordEntry.getKey();
                List<RowChangeLog> rowChangeLogs = new ArrayList<>();
                List<RecordImage> recordImages = recordEntry.getValue();
                for (RecordImage recordImage : recordImages) {
                    RecordImageFormat recordImageFormat = RecordImageFormatRegistry.getInstance().getRecordImageFormat(recordImage.getSqlType());
                    Map<SqlType, List<RowChangeLog>> rowChangeLogMap = recordImageFormat.getChangeLog(recordImage);
                    rowChangeLogMap.forEach((k, v) -> {
                        v.forEach(rowChangeLog -> {
                            if (CollectionUtil.isNotEmpty(rowChangeLog.getFieldChangeLogs())) {
                                rowChangeLog.setSqlType(k);
                                rowChangeLogs.add(rowChangeLog);
                            }
                        });
                    });
                }
                if (CollectionUtil.isNotEmpty(rowChangeLogs)) {
                    TableChangeLog tableChangeLog = TableChangeLog.builder().tableName(tableName).rowChangeLogs(rowChangeLogs).build();
                    tableChangeLogs.add(tableChangeLog);
                }
            }
            if (CollectionUtil.isNotEmpty(tableChangeLogs)) {
                AuditLogEventBus.getInstance().publish(TableChangeEvent.builder().tableChangeLogs(tableChangeLogs).operationId(context.getOperationId())
                        .userId(context.getUserId())
                        .userName(context.getUserName())
                        .cause(context.getTargetCause())
                        .metaData(context.getMetaData())
                        .operationName(context.getOperationName())
                        .statistician(context.getSqlStatistician()).build());
            } else {
                log.debug("数据库未做任何更改");
            }
        } catch (Exception e) {
            log.error("转换日志失败", e);
        }
    }
}
